package com.imyuanma.qingyun.common.config;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.imyuanma.qingyun.common.config.formatter.DateFormatter;
import com.imyuanma.qingyun.common.config.interceptor.ICommonInterceptor;
import com.imyuanma.qingyun.common.util.CollectionUtil;
import com.imyuanma.qingyun.common.util.DateUtil;
import com.imyuanma.qingyun.common.util.JsonUtil;
import com.imyuanma.qingyun.common.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;

/**
 * webmvc统一配置处理
 *
 * @author imrookie
 * @date 2018/10/1
 */
@Configuration
@ConditionalOnWebApplication
@ConditionalOnBean({QingYunMvcConfiguration.class})
public class QingYunWebMvcConfig extends WebMvcConfigurationSupport {

    private static final Logger logger = LoggerFactory.getLogger(QingYunWebMvcConfig.class);

    @Autowired
    private DateFormatter dateFormatter;

    @Autowired
    private QingYunMvcConfiguration mvcConfiguration;


    //对应上面的patterns常量
    private static final String RESOURCE_MAPPER_KEY_PATTERNS = "patterns";
    //对应上面的locations常量
    private static final String RESOURCE_MAPPER_KEY_LOCATIONS = "locations";

    /**
     * 注册拦截器
     *
     * @param registry
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        super.addInterceptors(registry);
        if (StringUtil.isBlank(mvcConfiguration.getInterceptorBeanNames())) {
            logger.warn("[QingYunWebMvc全局配置-拦截器] 未发现拦截器bean配置,若需开启拦截器,请配置[qingyun.mvc.interceptorBeanNames],多个拦截器bean使用逗号拼接");
            return;
        }

        String[] beanNameArr = mvcConfiguration.getInterceptorBeanNames().split(",");
        if (beanNameArr.length == 0) {
            logger.error("[QingYunWebMvc全局配置-拦截器] 参数[qingyun.mvc.interceptorBeanNames={}]解析为数组失败", mvcConfiguration.getInterceptorBeanNames());
            return;
        }

        //遍历,装载拦截器配置
        for (String beanName : beanNameArr) {
            if (StringUtil.isBlank(beanName)) {
                logger.warn("[QingYunWebMvc全局配置-拦截器] bean设置中存在beanName为空:[qingyun.mvc.interceptorBeanNames={}]", mvcConfiguration.getInterceptorBeanNames());
                continue;
            }
            //根据配置获取bean
            Object interceptor = SpringContextHolder.getBean(beanName);
            if (interceptor == null) {
                logger.error("[QingYunWebMvc全局配置-拦截器] 根据beanName={}获取bean实例失败", beanName);
                throw new RuntimeException("根据beanName=" + beanName + "获取bean实例失败");
            }
            InterceptorRegistration interceptorRegistration = null;
            if (interceptor instanceof ICommonInterceptor) {//公共拦截器接口
                //拦截器自行注册
                interceptorRegistration = ((ICommonInterceptor) interceptor).addInterceptors(registry);
            } else if (interceptor instanceof HandlerInterceptor) {//spring mvc原始拦截器接口
                //注册拦截器,默认url模式为/**
                interceptorRegistration = registry.addInterceptor((HandlerInterceptor) interceptor).addPathPatterns("/**");
            } else {
                logger.warn("[QingYunWebMvc全局配置-拦截器] 未知的拦截器实例类型:{},目前支持类型为:HandlerInterceptor,ICommonInterceptor", interceptor.getClass().getName());
            }
            //设置默认排除路径
            if (interceptorRegistration != null && CollectionUtil.isNotEmpty(mvcConfiguration.getDefaultInterceptorExcludePathPatterns())) {
                interceptorRegistration.excludePathPatterns(mvcConfiguration.getDefaultInterceptorExcludePathPatterns());
                logger.debug("[QingYunWebMvc全局配置-拦截器] 为拦截器[{}]设置默认排除路径:{},对应配置项[qingyun.mvc.defaultInterceptorExcludePathPatterns]", beanName, mvcConfiguration.getDefaultInterceptorExcludePathPatterns());
            }
            logger.info("[QingYunWebMvc全局配置-拦截器] 拦截器[{}]注册完成.", beanName);
        }
    }

    /**
     * 注册静态资源
     *
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        //第一个方法设置访问路径前缀，第二个方法设置资源路径
        //静态资源
        //registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
        //页面
        //registry.addResourceHandler("/page/**").addResourceLocations("classpath:/page/");
        //registry.addResourceHandler("/favicon.ico").addResourceLocations("classpath:/");
        logger.debug("[QingYunWebMvc全局配置-注册静态资源] 开始注册静态资源.");
        if (CollectionUtil.isEmpty(mvcConfiguration.getResourceMappers())) {
            logger.warn("[QingYunWebMvc全局配置-注册静态资源] 资源映射配置为空,请确认是否真的不需配置静态资源映射,对应配置项[qingyun.mvc.resource-mappers]");
            return;
        }
        for (Map<String, String[]> map : mvcConfiguration.getResourceMappers()) {
            if (CollectionUtil.isEmpty(map)) {
                continue;
            }
            String[] patterns = map.get(RESOURCE_MAPPER_KEY_PATTERNS);
            String[] locations = map.get(RESOURCE_MAPPER_KEY_LOCATIONS);
            if (CollectionUtil.isEmpty(patterns) || CollectionUtil.isEmpty(locations)) {
                logger.warn("[QingYunWebMvc全局配置-注册静态资源] 资源映射配置项无效,请检查配置是否正确,配置项={}", JsonUtil.toJson(map));
                continue;
            }
            registry.addResourceHandler(patterns).addResourceLocations(locations);
            logger.info("[QingYunWebMvc全局配置-注册静态资源] 成功注册静态资源,{}={},{}={}", RESOURCE_MAPPER_KEY_PATTERNS, patterns, RESOURCE_MAPPER_KEY_LOCATIONS, locations);
        }
        logger.debug("[QingYunWebMvc全局配置-注册静态资源] 注册静态资源完毕.");
    }

    /**
     * 注册视图控制器
     *
     * @param registry
     */
    @Override
    protected void addViewControllers(ViewControllerRegistry registry) {
        if (StringUtil.isNotBlank(mvcConfiguration.getWelcomeUrl())) {
            registry.addViewController("/").setViewName(mvcConfiguration.getWelcomeUrl());//"forward:" +
            registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
        }
    }

    /**
     * 格式化
     *
     * @param registry
     */
    @Override
    protected void addFormatters(FormatterRegistry registry) {
        //时间格式化
        registry.addFormatter(dateFormatter);
    }

    /**
     * 定义时间格式转换器
     *
     * @return
     */
    @Bean
    public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper mapper = objectMapper();
        converter.setObjectMapper(mapper);
        return converter;
    }

    /**
     * 添加转换器
     *
     * @param converters
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        //将时间格式转换器添加到转换器列表中
        converters.add(jackson2HttpMessageConverter());
    }

    private ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 不序列化null的属性
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DateUtil.FormatPattern.YYYY_MM_DD_HH_MI_SS.getPattern())));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DateUtil.FormatPattern.YYYY_MM_DD.getPattern())));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DateUtil.FormatPattern.HH_MI_SS.getPattern())));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DateUtil.FormatPattern.YYYY_MM_DD_HH_MI_SS.getPattern())));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DateUtil.FormatPattern.YYYY_MM_DD.getPattern())));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DateUtil.FormatPattern.HH_MI_SS.getPattern())));
        objectMapper.registerModule(javaTimeModule).registerModule(new ParameterNamesModule());
        objectMapper.setDateFormat(new SimpleDateFormat(DateUtil.FormatPattern.YYYY_MM_DD_HH_MI_SS.getPattern()));
        return objectMapper;
    }


}
